/* Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.activiti.engine.impl.bpmn.behavior;

import java.util.ArrayList;
import java.util.List;

import org.activiti.engine.ActivitiIllegalArgumentException;
import org.activiti.engine.delegate.JavaDelegate;
import org.activiti.engine.impl.TaskContext;
import org.activiti.engine.impl.context.Context;
import org.activiti.engine.impl.persistence.entity.ExecutionEntity;
import org.activiti.engine.impl.pvm.PvmTransition;
import org.activiti.engine.impl.pvm.delegate.ActivityExecution;
import org.activiti.engine.impl.pvm.process.ActivityImpl;

import com.frameworkset.util.StringUtil;


/**
 * @author Joram Barrez
 */
public class ParallelMultiInstanceBehavior extends MultiInstanceActivityBehavior {
  
  public ParallelMultiInstanceBehavior(ActivityImpl activity, AbstractBpmnActivityBehavior originalActivityBehavior) {
    super(activity, originalActivityBehavior);
  }
  
  /**
   * Handles the parallel case of spawning the instances.
   * Will create child executions accordingly for every instance needed.
   */
   protected void createInstances(ActivityExecution execution) throws Exception {
    int nrOfInstances = resolveNrOfInstances(execution);
    if (nrOfInstances <= 0) {
      throw new ActivitiIllegalArgumentException("Invalid number of instances: must be positive integer value" 
              + ", but was " + nrOfInstances);
    }
    
    setLoopVariable(execution, NUMBER_OF_INSTANCES, nrOfInstances);
    setLoopVariable(execution, NUMBER_OF_COMPLETED_INSTANCES, 0);
    setLoopVariable(execution, NUMBER_OF_ACTIVE_INSTANCES, nrOfInstances);
    
    List<ActivityExecution> concurrentExecutions = new ArrayList<ActivityExecution>();
    for (int loopCounter=0; loopCounter<nrOfInstances; loopCounter++) {
      ActivityExecution concurrentExecution = execution.createExecution();
      concurrentExecution.setActive(true);
      concurrentExecution.setConcurrent(true);
      concurrentExecution.setScope(false);
      
      // In case of an embedded subprocess, and extra child execution is required
      // Otherwise, all child executions would end up under the same parent,
      // without any differentation to which embedded subprocess they belong
      if (isExtraScopeNeeded()) {
        ActivityExecution extraScopedExecution = concurrentExecution.createExecution();
        extraScopedExecution.setActive(true);
        extraScopedExecution.setConcurrent(false);
        extraScopedExecution.setScope(true);
        concurrentExecution = extraScopedExecution;
      } 
      concurrentExecution.setTaskContext(execution.getTaskContext());
      
      concurrentExecutions.add(concurrentExecution);
      logLoopDetails(concurrentExecution, "initialized", loopCounter, 0, nrOfInstances, nrOfInstances);
    }
    
    // Before the activities are executed, all executions MUST be created up front
    // Do not try to merge this loop with the previous one, as it will lead to bugs,
    // due to possible child execution pruning.
    for (int loopCounter=0; loopCounter<nrOfInstances; loopCounter++) {
      ActivityExecution concurrentExecution = concurrentExecutions.get(loopCounter);
      // executions can be inactive, if instances are all automatics (no-waitstate)
      // and completionCondition has been met in the meantime
      if (concurrentExecution.isActive() && !concurrentExecution.isEnded() 
              && concurrentExecution.getParent().isActive() 
              && !concurrentExecution.getParent().isEnded()) { 
        setLoopVariable(concurrentExecution, LOOP_COUNTER, loopCounter);
        executeOriginalBehavior(concurrentExecution, loopCounter);
      }
    }
    
    // See ACT-1586: ExecutionQuery returns wrong results when using multi instance on a receive task
    // The parent execution must be set to false, so it wouldn't show up in the execution query
    // when using .activityId(something). Do not we cannot nullify the activityId (that would
    // have been a better solution), as it would break boundary event behavior.
    if (!concurrentExecutions.isEmpty()) {
      ExecutionEntity executionEntity = (ExecutionEntity) execution;
      executionEntity.setActive(false);
    }
  }
  
  /**
   * Called when the wrapped {@link ActivityBehavior} calls the 
   * {@link AbstractBpmnActivityBehavior#leave(ActivityExecution)} method.
   * Handles the completion of one of the parallel instances
   */
//  public void leave(ActivityExecution execution) {
//	   leave(execution,null); 
//  }
  
  /**
   * added by biaoping.yin
   */
  public void leave(ActivityExecution execution) {
    callActivityEndListeners(execution);
    TaskContext taskContext = execution.getTaskContext();
    int loopCounter = getLoopVariable(execution, LOOP_COUNTER);
    int nrOfInstances = getLoopVariable(execution, NUMBER_OF_INSTANCES);
    int nrOfCompletedInstances = getLoopVariable(execution, NUMBER_OF_COMPLETED_INSTANCES) + 1;
    int nrOfActiveInstances = getLoopVariable(execution, NUMBER_OF_ACTIVE_INSTANCES) - 1;
    
    if (isExtraScopeNeeded()) {
      // In case an extra scope was created, it must be destroyed first before going further
      ExecutionEntity extraScope = (ExecutionEntity) execution;
      execution = execution.getParent();
      extraScope.remove();
    }
    
    setLoopVariable(execution.getParent(), NUMBER_OF_COMPLETED_INSTANCES, nrOfCompletedInstances);
    setLoopVariable(execution.getParent(), NUMBER_OF_ACTIVE_INSTANCES, nrOfActiveInstances);
    logLoopDetails(execution, "instance completed", loopCounter, nrOfCompletedInstances, nrOfActiveInstances, nrOfInstances);
   
    ExecutionEntity executionEntity = (ExecutionEntity) execution;
    boolean customDTask = (taskContext.getDestinationTaskKey() != null 
    		&& !taskContext.getDestinationTaskKey().equals("")) 
    		&& !taskContext.isFromreject();
    String deleteReason = executionEntity.getDeleteReason();
   
    
    executionEntity.inactivate();
    executionEntity.getParent().forceUpdate();
   
    
    List<ActivityExecution> joinedExecutions = executionEntity.findInactiveConcurrentExecutions(execution.getActivity(),customDTask);
//    List<ActivityExecution> joinedExecutions = executionEntity.findInactiveConcurrentExecutions(execution.getActivity());
    
    if (joinedExecutions.size() == nrOfInstances || completionConditionSatisfied(execution) || customDTask) {
      
      // Removing all active child executions (ie because completionCondition is true)
      List<ExecutionEntity> executionsToRemove = new ArrayList<ExecutionEntity>();
//      if(!customDTask)
      {
	      for (ActivityExecution childExecution : executionEntity.getParent().getExecutions()) {
	        if (childExecution.isActive()) {
	          executionsToRemove.add((ExecutionEntity) childExecution);
	        }
	      }
      }
      for (ExecutionEntity executionToRemove : executionsToRemove) {
        if (LOGGER.isDebugEnabled()) {
          LOGGER.debug("Execution {} still active, but multi-instance is completed. Removing this execution.", executionToRemove);
        }
        executionToRemove.inactivate();
        if(!customDTask)
        	executionToRemove.deleteCascade("multi-instance completed",execution.getBussinessop(),execution.getBussinessRemark());
        else
        {
        	
        	executionToRemove.deleteCascade(deleteReason,execution.getBussinessop(),execution.getBussinessRemark());
        }
      }
     
	    /**
	       * 解决多实例任务没有执行处理流出路径的条件运算问题
	       */
	    List<PvmTransition> transitionsToTake = this.bpmnActivityBehavior.evalOutgoingTransition(execution,executionEntity);
	    
//      executionEntity.takeAll(executionEntity.getActivity().getOutgoingTransitions(), joinedExecutions,destinationTaskKey);
	    executionEntity.takeAll(transitionsToTake, joinedExecutions, taskContext);
    } 
  }

@Override
public void execute(ActivityExecution execution) throws Exception {
	if(execution.getTaskContext().isCOPY() || execution.getTaskContext().isNotify())
	  {
		 
		  Context.getCommandContext().getHistoryManager()
	      .recordCopyUseTaskActivityComplete((ExecutionEntity) execution);
			String BUSSINESSCONTROLCLASS = TaskContext.CopyTaskBehavior;
			JavaDelegate javaDelegate = Context.getJavaDelegate(BUSSINESSCONTROLCLASS);
			super.execute(execution, javaDelegate);
			
			super.leave(execution);
	  }
	  else if(execution.getTaskContext().isHasassignee())
	{
			super.execute(execution);
	}
	else
	{
		Context.getCommandContext().getHistoryManager()
	      .recordUseTaskActivityAutoComplete((ExecutionEntity) execution);
		String BUSSINESSCONTROLCLASS = execution.getTaskContext().getBUSSINESSCONTROLCLASS();
		if(StringUtil.isNotEmpty(BUSSINESSCONTROLCLASS))
		{
			JavaDelegate javaDelegate = Context.getJavaDelegate(BUSSINESSCONTROLCLASS);
			super.execute(execution, javaDelegate);
		}
		super.leave(execution);
			
	}// TODO Auto-generated method stub
	
}


}
